//
// Created by LONG on 2020/10/19.
//

#include "../include/l1Api.h"
#include "l1Ul/l1Ul.h"
#include "l1Dl/l1Dl.h"
#include "../../lcore/misc/misc.h"

S_UeL1Context *gpUeL1Context = NULL;

int32_t UE_L1_LoadUeInstConfig(S_UeL1UeInst *pUeInst, S_UeInstContext *pUeInstContext)
{
    pUeInst->inUse = pUeInstContext->inUse;

    pUeInst->pL2L1CpMsgQue = pUeInstContext->pL2L1CpMsgQue;
    pUeInst->pL1L2CpMsgQue = pUeInstContext->pL1L2CpMsgQue;
    pUeInst->pL2L1UpMsgQue = pUeInstContext->pL2L1UpMsgQue;
    pUeInst->pL1L2UpMsgQue = pUeInstContext->pL1L2UpMsgQue;

    pUeInst->slotNum = 20;

    return O5G_RC_OK;
}

int32_t UE_L1_LoadConfig()
{
    int32_t rc;
    uint32_t loop;

    gpUeL1Context->ueInstNum = gpUeContext->ueInstNum;
    for (loop = 0; loop < gpUeContext->ueInstNum; loop++)
    {
        rc = UE_L1_LoadUeInstConfig(&gpUeL1Context->ueInst[loop], &gpUeContext->ueInstContext[loop]);
        IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_L1_LoadConfig() UE_L1_LoadUeInstConfig() error!\n");
    }

    return O5G_RC_OK;
}

int32_t UE_L1_Init(void)
{
    int32_t rc;
    uint32_t ueLoop;
    uint32_t ttiBuffLoop;
    S_UeL1UeInst *pUeInst;
    char ttiIntPipeName[256];

    gpUeL1Context = OS_MemCalloc(1, sizeof(S_UeL1Context));
    IF_RETURN(==, NULL, gpUeL1Context, O5G_RC_ER, "UE_L1_Init() OS_MemCalloc() error!\n");

    rc = UE_L1_LoadConfig();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_L1_Init() UE_L1_LoadConfig() error!\n");

    UE_INIT_INST_IDX(gpUeL1Context);

    for (ueLoop = 0; ueLoop < UE_UE_INST_NUM_MAX; ueLoop++)
    {
        pUeInst = &gpUeL1Context->ueInst[ueLoop];
        IF_CONTINUE(==, FALSE, pUeInst->inUse, NO_LOG_MESSAGE);

        snprintf(ttiIntPipeName, sizeof(ttiIntPipeName), "%s_ue%u", UE_TTI_INT_PIPE_NAME, ueLoop);
        rc = OS_SelectInit(&pUeInst->ttiIntSelector, ttiIntPipeName);
        IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_L1_Init() OS_SelectInit(ttiIntSelector) error!\n");
        CSP_LOG_INF(UE_MD_L2_COMM, "UE_L1_Init() ue[%u] TTI_INT_PIPE[%s].\n", ueLoop, (char *)ttiIntPipeName);

        pUeInst->pRanUeQue = VarRB_Create("RanUeQueShm", 4 * 1024 * 1024, VAR_RB_TYPE_SPSC);
        IF_RETURN(==, NULL, pUeInst->pRanUeQue, O5G_RC_ER, "UE_L1_Init() VarRB_Create(pRanUeQue) error!\n");
        pUeInst->pUeRanQue = VarRB_Create("UeRanQueShm", 4 * 1024 * 1024, VAR_RB_TYPE_SPSC);
        IF_RETURN(==, NULL, pUeInst->pUeRanQue, O5G_RC_ER, "UE_L1_Init() VarRB_Create(pUeRanQue) error!\n");

        for (ttiBuffLoop = 0; ttiBuffLoop < UE_L1_TTI_BUFF_NUM; ttiBuffLoop++)
        {
            pUeInst->pUlTtiBuff[ttiBuffLoop] = VarRB_Create(NULL, 4 * 1024 * 1024, VAR_RB_TYPE_MPSC);
            IF_RETURN(==, NULL, pUeInst->pUlTtiBuff[ttiBuffLoop], O5G_RC_ER, "UE_L1_Init() VarRB_Create() error!\n");
            pUeInst->pDlTtiBuff[ttiBuffLoop] = VarRB_Create(NULL, 4 * 1024 * 1024, VAR_RB_TYPE_MPSC);
            IF_RETURN(==, NULL, pUeInst->pDlTtiBuff[ttiBuffLoop], O5G_RC_ER, "UE_L1_Init() VarRB_Create() error!\n");
        }
    }

    CSP_LOG_INF(UE_MD_L1_COMM, "UE_L1_Init() success.\n");

    return O5G_RC_OK;
}

int32_t UE_L1_Final(void)
{
    int32_t rc;
    uint32_t ueLoop;
    uint32_t ttiBuffLoop;
    S_UeL1UeInst *pUeInst;

    for (ueLoop = 0; ueLoop < UE_UE_INST_NUM_MAX; ueLoop++)
    {
        pUeInst = &gpUeL1Context->ueInst[ueLoop];
        IF_CONTINUE(==, FALSE, pUeInst->inUse, NO_LOG_MESSAGE);
        rc = OS_SelectFinal(&pUeInst->ttiIntSelector);
        IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_L1_Final() OS_SelectFinal(ttiIntSelector) error!\n");

        VarRB_Destroy(pUeInst->pRanUeQue);
        VarRB_Destroy(pUeInst->pUeRanQue);

        for (ttiBuffLoop = 0; ttiBuffLoop < UE_L1_TTI_BUFF_NUM; ttiBuffLoop++)
        {
            VarRB_Destroy(pUeInst->pUlTtiBuff[ttiBuffLoop]);
            VarRB_Destroy(pUeInst->pDlTtiBuff[ttiBuffLoop]);
        }
    }

    OS_MemFree(gpUeL1Context);

    CSP_LOG_INF(UE_MD_L1_COMM, "UE_L1_Final() success.\n");

    return O5G_RC_OK;
}

void UE_L1_UeProcess(S_UeL1UeInst *pUeInst)
{
    int32_t rc;
    uint32_t size;
    S_UeCommMsgHdr *pCommMsgHdr;

    while (1)
    {
        pCommMsgHdr = VarRB_ReadRequest(pUeInst->pL2L1CpMsgQue, &size);
        IF_BREAK(==, NULL, pCommMsgHdr, NO_LOG_MESSAGE);

        switch (pCommMsgHdr->type)
        {
        case UE_L1_API_MSG_CONFIG_REQ:
        {
            CSP_LOG_INF(UE_MD_L1_COMM, "UE_L1_UeProcess() recv UE_L1_API_MSG_CONFIG_REQ\n");

            S_UeL1ApiMsgConfigRsp configRsp;

            configRsp.commMsgHdr.type = UE_L1_API_MSG_CONFIG_RSP;
            configRsp.commMsgHdr.size = sizeof(configRsp);
            configRsp.ueIdx = ((S_UeL1ApiMsgConfigRsp *)pCommMsgHdr)->ueIdx;
            VRB_PUT(rc, pUeInst->pL1L2CpMsgQue, &configRsp, sizeof(configRsp));
        }
            break;
        case UE_L1_API_MSG_SYNC_REQ:
        {
            CSP_LOG_INF(UE_MD_L1_COMM, "UE_L1_UeProcess() recv UE_L1_API_MSG_SYNC_REQ\n");

            S_UeL1ApiMsgSyncRsp syncRsp;

            syncRsp.commMsgHdr.type = UE_L1_API_MSG_SYNC_RSP;
            syncRsp.commMsgHdr.size = sizeof(syncRsp);
            syncRsp.ueIdx = ((S_UeL1ApiMsgSyncRsp *)pCommMsgHdr)->ueIdx;
            VRB_PUT(rc, pUeInst->pL1L2CpMsgQue, &syncRsp, sizeof(syncRsp));

            pUeInst->appState = UE_L1_APP_STATE_SYNC;
        }
            break;
        default:
            CSP_LOG_WRN(UE_MD_L1_COMM, "UE_L1_CellCpProcess() recv unsupport msg[%#x]\n", pCommMsgHdr->type);
            break;
        }
        VarRB_ReadConsume(pUeInst->pL2L1CpMsgQue, pCommMsgHdr);
    }
}

void UE_L1_Process()
{
    uint32_t ueLoop;
    S_UeL1UeInst *pUeInst;

    for (ueLoop = 0; ueLoop < UE_UE_INST_NUM_MAX; ueLoop++)
    {
        pUeInst = &gpUeL1Context->ueInst[ueLoop];
        IF_CONTINUE(==, FALSE, pUeInst->inUse, NO_LOG_MESSAGE);
        UE_L1_UeProcess(pUeInst);
    }

    UE_L1_UL_Process();
    usleep(1000);
    UE_L1_DL_Process();
}

void *UE_L1_Task(void* arg)
{
    while (1)
    {
        UE_L1_Process();
        pthread_testcancel();
    }
    return NULL;
}

int32_t UE_L1_Start(void)
{
    int32_t rc;

    rc = OS_ThreadCreate(&gpUeL1Context->tid, UE_L1_Task, NULL);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_L1_Start() OS_ThreadCreate() error!\n");

    CSP_LOG_INF(UE_MD_L1_COMM, "UE_L1_Start() success.\n");

    return O5G_RC_OK;
}

int32_t UE_L1_Stop(void)
{
    int32_t rc;

    rc = OS_ThreadCancel(gpUeL1Context->tid);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_L1_Stop() OS_ThreadCancel() error!\n");

    CSP_LOG_INF(UE_MD_L1_COMM, "UE_L1_Stop() success.\n");

    return O5G_RC_OK;
}
